package com.btb.adapter.service;

import java.math.BigInteger;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.security.auth.message.callback.PrivateKeyCallback.Request;

import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.btb.adapter.constant.Constants;
import com.btb.adapter.constant.EventType;
import com.btb.adapter.constant.QueryType;
import com.btb.adapter.contract.ActFishService;
import com.btb.adapter.contract.ActHeroService;
import com.btb.adapter.mapper.OffChainMapper;
import com.btb.adapter.mapper.UpChainFishMapper;
import com.btb.adapter.mapper.UpChainHeroMapper;
import com.btb.adapter.model.OffChain;
import com.btb.adapter.model.OffChainExample;
import com.btb.adapter.model.TaskConfig;
import com.btb.adapter.model.UpChainFish;
import com.btb.adapter.model.UpChainFishExample;
import com.btb.adapter.model.UpChainHero;
import com.btb.adapter.model.UpChainHeroExample;
import com.btb.adapter.utils.SpringContextUtil;
import com.btb.adapter.vo.OffChainEventEventResponse;
import com.btb.adapter.vo.UpChainEventEventResponse;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class UpChainEvent {
	@Autowired
	private WatchTransferService watchTransferService;
	@Autowired
	private TaskConfigService taskConfigService;
	@Autowired
	private ActFishService actFishService;
	@Autowired
	private ActHeroService actHeroService;
	@Autowired
	private UpChainHeroMapper upChainHeroMapper;
	@Autowired
	private UpChainFishMapper upChainFishMapper;
	@Autowired
	private OffChainMapper offChainMapper;
	@Autowired
	private ChainblockEventService chainblockEventService;

	public void start(EventType type, String taskName) {
		try {
			final TaskConfig taskConfig = taskConfigService.findConfigByTaskNameCache(taskName);
			if (Objects.isNull(taskConfig)) {
				log.error("{} taskConfig is null", taskName);
				return;
			}
			Long status = Optional.ofNullable(taskConfig.getStatus()).orElse(1L);
			if (Objects.equals(1L, status)) {
				log.error("{} taskConfig status is 1 stop", taskName);
				return;
			}
			BigInteger maxBlockNumber = watchTransferService.getBlockNumber();
			Long startBlockNumber = taskConfig.getStartBlockNumber();
			if (startBlockNumber.longValue() >= maxBlockNumber.longValue()) {
				log.error(
						"WatchTransferJob id:{} startBlockNumber > blockNumber is stop startBlockNumber:{},blockNumber:{}",
						taskConfig.getId(), startBlockNumber, maxBlockNumber);
				return;
			}
			Long stepNum = taskConfig.getStepNum();
			int end = startBlockNumber.intValue() + stepNum.intValue();
			int newEnd = Math.min(end, maxBlockNumber.intValue());
			subEvent(type, String.valueOf(startBlockNumber), String.valueOf(newEnd));
			taskConfigService.updateStartBlockNumber(taskConfig.getId(), new Long(newEnd));
		} catch (Exception e) {
			log.error("BuySuccessSchedule err:{}", e.getMessage(), e);
		}
	}
	private void subEvent(EventType type, final String startBlockNumber, final String endBlockNumber) {
		List<UpChainEventEventResponse> upChainEvent = null;
		if (type == EventType.HERO_CHAIN_UP) {
			upChainEvent = actHeroService.upChainEvent(startBlockNumber, endBlockNumber);
			processHero(upChainEvent);
		} else if (type == EventType.FISH_CHAIN_UP) {
			upChainEvent = actFishService.upChainEvent(startBlockNumber, endBlockNumber);
			processFish(upChainEvent);
		}else if (type == EventType.OFF_CHAIN) {
			final List<OffChainEventEventResponse> offChainHeroEvent = actHeroService.offChainEvent(startBlockNumber, endBlockNumber);
			final List<OffChainEventEventResponse> offChainFishEvent = actFishService.offChainEvent(startBlockNumber, endBlockNumber);
			process(offChainHeroEvent, offChainFishEvent);
		} else {
			log.error("other type err: {}", type);
		}
	}
	private void processHero(final List<UpChainEventEventResponse> upChainEvent) {
		if (Objects.isNull(upChainEvent)) {
			return;
		}
		upChainEvent.forEach(x -> {updateHeroStatus(x);});
	}
	private void processFish(final List<UpChainEventEventResponse> upChainEvent) {
		if (Objects.isNull(upChainEvent)) {
			return;
		}
		upChainEvent.forEach(x -> {updateFishStatus(x);});
	}

	public void updateHeroStatus(UpChainEventEventResponse response) {
		UpChainHeroExample queryExample = new UpChainHeroExample();
		queryExample.createCriteria().andStatusEqualTo(Constants.UP_CHAIN_STATUS_TWO).andClientIdEqualTo(response._tokenId.intValue());
		List<UpChainHero> selectByExample = upChainHeroMapper.selectByExample(queryExample);
		final UpChainHero upChainHero = selectByExample.stream().findAny().orElse(null);
		if(Objects.isNull(upChainHero)) {
			return;
		}
		final String transactionHash = upChainHero.getTransactionHash();
		final Integer id = upChainHero.getId();
		if(Objects.isNull(transactionHash)) {
			return;
		}
		String status = status(transactionHash);
		UpChainHero record = UpChainHero.builder()
				.id(id)
				.eventUpChainTimestamp(new Date().getTime())
				.status(status)
				.build();
		upChainHeroMapper.updateByPrimaryKeySelective(record);
	}
	public void updateFishStatus(UpChainEventEventResponse response) {
		UpChainFishExample queryExample = new UpChainFishExample();
		queryExample.createCriteria().andStatusEqualTo(Constants.UP_CHAIN_STATUS_TWO).andClientIdEqualTo(response._tokenId.intValue());
		List<UpChainFish> selectByExample = upChainFishMapper.selectByExample(queryExample);
		final UpChainFish upChainFish = selectByExample.stream().findAny().orElse(null);
		if(Objects.isNull(upChainFish)) {
			return;
		}
		final String transactionHash = upChainFish.getTransactionHash();
		final Long id = upChainFish.getId();
		if(Objects.isNull(transactionHash)) {
			return;
		}
		String status = status(transactionHash);
		final UpChainFish record = UpChainFish.builder()
				.id(id)
				.eventUpChainTimestamp(new Date().getTime())
				.status(status)
				.build();
		upChainFishMapper.updateByPrimaryKeySelective(record);
	}

	private String status(final String transactionHash) {
		boolean query = chainblockEventService.query(transactionHash);
		String status = Constants.UP_CHAIN_STATUS_FOUE;
		//查到如果是成功,就是上链成功的
		if(query) {
			status = Constants.UP_CHAIN_STATUS_THREE;
		}
		return status;
	}
	private void process(List<OffChainEventEventResponse> offChainHeroEvent, List<OffChainEventEventResponse> offChainFishEvent) {
		if(Objects.nonNull(offChainHeroEvent)) {
			offChainHeroEvent.forEach(x->{process(x);});
		}
		if(Objects.nonNull(offChainFishEvent)) {
			offChainFishEvent.forEach(x->{process(x);});
		}
	}

	private void process(OffChainEventEventResponse response) {
		OffChainExample queryExample = new OffChainExample();
		queryExample.createCriteria().andStatusEqualTo(Constants.OFF_CHAIN_STATUS_TWO).andClientIdEqualTo(response._clientid.toString());
		List<OffChain> selectByExample = offChainMapper.selectByExample(queryExample);
		final OffChain offChain = selectByExample.stream().findAny().orElse(null);
		if(Objects.isNull(offChain)) {
			return;
		}
		final String transactionHash = offChain.getTransactionHash();
		final Long id = offChain.getId();
		if(Objects.isNull(transactionHash)) {
			return;
		}
		String status = status(transactionHash);
		final OffChain record = OffChain.builder()
				.id(id)
				.eventUpChainTimestamp(new Date().getTime())
				.status(status)
				.build();
		offChainMapper.updateByPrimaryKeySelective(record);
	}
}
